package com.fund.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fund.common.enums.TradeStatus;
import com.fund.common.pojo.PageResult;
import com.fund.common.pojo.Result;
import com.fund.common.utils.UserContext;
import com.fund.product.constants.ProductConstants;
import com.fund.product.mapper.ProductMapper;
import com.fund.product.pojo.dto.ProductPageDTO;
import com.fund.product.pojo.entity.NetWorth;
import com.fund.product.pojo.entity.Product;
import com.fund.product.pojo.vo.ProductVO;
import com.fund.product.service.NetWorthService;
import com.fund.product.service.ProductService;
import com.fund.product.utils.ProductCodeGenerator;
import io.swagger.models.auth.In;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {

    @Autowired
    ProductMapper productMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    NetWorthService netWorthService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    private static final Integer BATCH_SIZE = 1000;

    /**
     * 分页查询
     * @param productDTO
     * @return
     */
    @Override
    public Result<PageResult<ProductVO>> pageQuery(ProductPageDTO productDTO) {
        //判断分页格式
        if(productDTO==null || productDTO.getCurrentPage()==null || productDTO.getCurrentPage()<=0
        || productDTO.getPageSize() <= 0){
            return Result.fail("错误的分页参数");
        }
        //构建查询条件
        LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(productDTO.getCode()!=null,Product::getCode,productDTO.getCode())
                .like(productDTO.getName()!=null,Product::getName,productDTO.getName())
                .like(productDTO.getCompany()!=null,Product::getCompany,productDTO.getCompany())
                .eq(productDTO.getStatus()!=null,Product::getStatus,productDTO.getStatus())
                .eq(productDTO.getType()!=null,Product::getType,productDTO.getType());
        //构建分页
        Page<Product> page = new Page<>(productDTO.getCurrentPage(), productDTO.getPageSize());
        //查询
        Page<Product> productPage = productMapper.selectPage(page, wrapper);
        //是否为空
        if(productPage.getRecords() == null || productPage.getRecords().isEmpty()){
            return Result.success(new PageResult<>(page.getTotal(),new ArrayList<>()));
        }
        //分配给VO
        ArrayList<ProductVO> productVOS = new ArrayList<>();
        for(Product product : productPage.getRecords()){
            ProductVO productVO = new ProductVO();
            BeanUtils.copyProperties(product,productVO);
            productVOS.add(productVO);
        }

        //返回
        return Result.success(new PageResult<>(productPage.getTotal(),productVOS));
    }

    @Override
    public Result<Boolean> saveOrUpdateProduct(Product product) {
        //判断名字是否合规
        if(product.getName()==null || product.getName().length() < ProductConstants.MIN_NAME_LENGTH
                || product.getName().length() > ProductConstants.MAX_NAME_LENGTH){
            return Result.fail("产品名长度必须在"+ProductConstants.MIN_NAME_LENGTH
                    + "和" + ProductConstants.MAX_NAME_LENGTH+ "之间");
        }
        if(product.getCompany() == null || product.getCompany().isEmpty()
                || product.getCompany().length() > ProductConstants.MAX_NAME_LENGTH
        ){
            return Result.fail("公司名不得为空，且长度不得超过" + ProductConstants.MAX_NAME_LENGTH);
        }
        //判断日期是否正确
        if (product.getInceptionDate().after(new Date())) {
            return Result.fail("成立日期不得超过当前日期");
        }
        product.setUpdateTime(LocalDateTime.now());
        //添加额外字段
        Long userId = UserContext.getUser();
        product.setUpdateUser(userId);
        //如果是新增请求
        if(product.getId() == null){
            product.setCreateTime(LocalDateTime.now());
            product.setCreateUser(userId);
            product.setStatus(TradeStatus.NORMAL);
            //生成产品代码
            String code = new ProductCodeGenerator(stringRedisTemplate).generateProductCode(product.getType());
            product.setCode(code);
            //插入数据
            return Result.success(this.save(product));
        }
        //如果是修改请求
        return Result.success(this.updateById(product));
    }

    @Override
    public Result<Boolean> deleteProductById(Integer id) {
        boolean removed = removeById(id);
        if(!removed){
            return Result.fail("该产品不存在");
        }

        return Result.success(true);
    }

    /**
     * 基金代码模糊查询
     * @param fundQuery
     * @return
     */
    @Override
    public Result<List<ProductVO>> findByCode(String fundQuery) {
        //只返回前五个（分页）
        IPage<Product> page = new Page<>(1, 5);
        //查询
        List<Product> productList = lambdaQuery().likeRight(Product::getCode, fundQuery).page(page).getRecords();
        List<ProductVO> productVOS = new ArrayList<>();
        for(Product product : productList){
            ProductVO productVO = new ProductVO();
            BeanUtils.copyProperties(product,productVO);
            productVOS.add(productVO);
        }
        return Result.success(productVOS);
    }

    /**
     * 根据id查询产品
     * @param id
     * @return
     */
    @Override
    public Result<ProductVO> getProductById(Long id) {
        Product product = getById(id);
        ProductVO productVO = new ProductVO();
        BeanUtils.copyProperties(product,productVO);
        return Result.success(productVO);
    }

    /**
     * 更新所有产品的净值
     * @return
     */
    @Override
    public Result<Boolean> updateAllWorth() {
        //因为产品数量可能较大，所以需要分批处理，用记录id+分页
        //记录上次更新到的id
        long lastId = 0L;
        while(true){
            //更新当前页的数据，即从lastId开始查询batch_size个
            Page<Product> page = lambdaQuery()
                    .gt(Product::getId, lastId)
                    .page(new Page<>(1, BATCH_SIZE));
            List<Product> products = page.getRecords();
            //如果查出数据为空，那么说明已经处理完成
            if(products == null || products.isEmpty()){
                break;
            }
            //存储净值数据
            List<NetWorth> netWorthList = new ArrayList<>();
            //更新净值，更新其他字段
            products.forEach(product -> {
                //设置更新值为0.9 - 1.1 之间的随机数
                double factor = 0.9 + 0.2 * ThreadLocalRandom.current().nextDouble();
                BigDecimal newWorth = product.getLatestWorth()
                        .multiply(BigDecimal.valueOf(factor)).setScale(2, RoundingMode.HALF_UP);
                product.setLatestWorth(newWorth);
                //设置其他字段
                product.setUpdateTime(LocalDateTime.now());
                product.setUpdateUser(1L);
                //添加到历史净值数据的更新表中
                NetWorth netWorth = new NetWorth();
                netWorth.setDate(new Date());
                netWorth.setNetWorth(newWorth);
                netWorth.setFundId(product.getId());
                netWorthList.add(netWorth);
            });
            //更新本轮的产品
            updateBatchById(products);

            //更新历史净值表
            rabbitTemplate.convertAndSend("worth.direct","worth.update",netWorthList);

            //将lastId设为本轮更新到的最后一个id
            lastId = products.get(products.size() - 1).getId();
        }
        return Result.success(true);
    }

    /**
     * 根据id列表查询产品最新净值
     * @param idList
     * @return
     */
    @Override
    public Result<HashMap<Long, BigDecimal>> getNetWorthByIds(List<Long> idList) {
        List<Product> productList = lambdaQuery().in(Product::getId, idList).list();
        HashMap<Long, BigDecimal> netWorthMap = new HashMap<>();
        for(Product product : productList){
            netWorthMap.put(product.getId(), product.getLatestWorth());
        }
        return Result.success(netWorthMap);
    }
}
